using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project4
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private SpriteFont font;

        private Texture2D projectile;
        private Texture2D gun;

        private Texture2D axis;
        private Texture2D cross;
        private Texture2D dot;

        private Vector2 offset;

        private Vector3 projectilePos;
        private Vector3 gunPos;
        private Vector3 targetPos;
        private Vector3 range;

        private List<VertexPositionColor> vertices;

        private String title;
        private String x;
        private String y;
        private String z;
        private String other;

        private InputProcessor input;


        private bool isStop = true;

        private float time = 0;
        private float velocity;
        private const float g = 9.81f;
        private double correctAlpha;
        private double correctGamma;

        private VertexBuffer vertexBuffer;

        private BasicEffect basicEffect;
        private Matrix world = Matrix.CreateTranslation(0, 0, 0);
        private Matrix view;
        private Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 800f / 800f, 0.01f, 10000f);

        private float rotate = 0;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 800;
            graphics.PreferredBackBufferWidth = 800;
            IsMouseVisible = true;

            input = new InputProcessor(this);

            Components.Add(input);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            font = Content.Load<SpriteFont>("font");

            projectile = Content.Load<Texture2D>("projectile");
            CreateRectangle(20, 10, out gun, Color.Blue);
            CreateRectangle(10, 3, out cross, Color.Red);
            CreateRectangle(800, 2, out axis, Color.Black);
            CreateRectangle(2, 2, out dot, Color.Red);

            offset = new Vector2(400, 400);

            projectilePos = new Vector3(-400, 0, -100);
            gunPos = new Vector3(-400, 0, -100);
            targetPos = new Vector3(400, 0, 100);
            range = Vector3.Zero;

            velocity = 100;

            title = "Position\nProjectile\nTarget\nRange";

            basicEffect = new BasicEffect(GraphicsDevice);

            //VertexPositionColor[] vertices = new VertexPositionColor[22];
            vertices = new List<VertexPositionColor>();
            vertices.Add(new VertexPositionColor(new Vector3(0, 0, 0), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(0, 400, 0), Color.Black));

            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-200, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-200, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(0, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(0, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(200, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(200, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, -200), Color.Black));

            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, -200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, -100), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, -100), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, 0), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, 0), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, 100), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, 100), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(-400, 0, 200), Color.Black));
            vertices.Add(new VertexPositionColor(new Vector3(400, 0, 200), Color.Black));//21

            vertices.Add(new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z + 10), Color.Red));
            vertices.Add(new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z - 10), Color.Red));
            vertices.Add(new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z - 10), Color.Red));
            vertices.Add(new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z + 10), Color.Red));//25

            vertices.Add(new VertexPositionColor(new Vector3(gunPos.X + 10, 0, gunPos.Z + 10), Color.Blue));
            vertices.Add(new VertexPositionColor(new Vector3(gunPos.X - 10, 0, gunPos.Z - 10), Color.Blue));
            vertices.Add(new VertexPositionColor(new Vector3(gunPos.X + 10, 0, gunPos.Z - 10), Color.Blue));
            vertices.Add(new VertexPositionColor(new Vector3(gunPos.X - 10, 0, gunPos.Z + 10), Color.Blue));//29

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();
            //vertices.

            // TODO: Add your update logic here
            if (input.isKeyDown(Keys.Left)) rotate += 0.01f;
            if (input.isKeyDown(Keys.Right)) rotate -= 0.01f;
            view = Matrix.CreateLookAt(new Vector3(1000 * (float)Math.Sin(rotate), 500, 1000 * (float)Math.Cos(rotate)), Vector3.Zero, Vector3.Up);

            if (input.isKeyPressed(Keys.Space)) isStop = !isStop;
            if (input.isKeyPressed(Keys.Back))
            {
                isStop = true;
                time = 0;

                //trail.Clear();
                vertices.RemoveRange(30, vertices.Count - 30);
            }

            if (isStop && time == 0)
            {
                if (input.isKeyPressed(Keys.E)) velocity += 10;
                if (input.isKeyPressed(Keys.Q)) velocity -= 10;
                if (input.isKeyPressed(Keys.D) && targetPos.X < 400)
                {
                    targetPos.X += 10;
                    vertices[22] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z + 10), Color.Red);
                    vertices[23] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[24] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[25] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z + 10), Color.Red);
                }
                if (input.isKeyPressed(Keys.A) && targetPos.X > 200)
                {
                    targetPos.X -= 10;
                    vertices[22] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z + 10), Color.Red);
                    vertices[23] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[24] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[25] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z + 10), Color.Red);
                }
                if (input.isKeyPressed(Keys.S) && targetPos.Z < 100)
                {
                    targetPos.Z += 10;
                    vertices[22] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z + 10), Color.Red);
                    vertices[23] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[24] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[25] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z + 10), Color.Red);
                }
                if (input.isKeyPressed(Keys.W) && targetPos.Z > -100)
                {
                    targetPos.Z -= 10;
                    vertices[22] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z + 10), Color.Red);
                    vertices[23] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[24] = new VertexPositionColor(new Vector3(targetPos.X + 10, 0, targetPos.Z - 10), Color.Red);
                    vertices[25] = new VertexPositionColor(new Vector3(targetPos.X - 10, 0, targetPos.Z + 10), Color.Red);
                }
                velocity = MathHelper.Clamp(velocity, 100, 200);
                //targetPos.X = MathHelper.Clamp(targetPos.X, 200, 400);
                //targetPos.Z = MathHelper.Clamp(targetPos.Z, -100, 100);


                range.X = targetPos.X - gunPos.X;
                range.Z = targetPos.Z - gunPos.Z;

                correctAlpha = Math.Asin(9.81 * Math.Sqrt(range.X * range.X + range.Z * range.Z) / velocity / velocity) / 2;
                correctGamma = Math.Asin(range.Z / Math.Sqrt(range.X * range.X + range.Z * range.Z));

                correctAlpha = (float)Math.PI / 2 - correctAlpha;
                //correctAlpha = 0.4711847406;
            }

            if (!isStop)
            {
                time *= 1000;
                time += gameTime.ElapsedGameTime.Milliseconds;
                time /= 1000;

                //trail.Add(projectilePos);

                vertices.Add(new VertexPositionColor(projectilePos, Color.Red));
            }

            projectilePos.X = (float)(-400 + velocity * Math.Sin(correctAlpha) * Math.Cos(correctGamma) * time);
            projectilePos.Y = (float)(velocity * Math.Cos(correctAlpha) * time - g * time * time / 2);
            projectilePos.Z = (float)(-100 + velocity * Math.Sin(correctAlpha) * Math.Sin(correctGamma) * time);

            if (projectilePos.Y <= 0) isStop = true;

            x = "x(m)\n" + projectilePos.X + "\n" + targetPos.X + "\n" + range.X;
            y = "y(m)\n" + projectilePos.Y.ToString("0.0000") + "\n" + targetPos.Y + "\n" + range.Y;
            z = "z(m)\n" + projectilePos.Z + "\n" + targetPos.Z + "\n" + range.Z;

            other = "Correct Alpha:" + MathHelper.ToDegrees((float)(Math.PI / 2 - correctAlpha))
                + "\nCorrect Gamma:" + MathHelper.ToDegrees((float)correctGamma)
                + "\nTime=" + time + "s\n"
                + "Velocity=" + velocity + "m/s\n";

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();

            spriteBatch.DrawString(font, title, new Vector2(350, 700), Color.White);
            spriteBatch.DrawString(font, x, new Vector2(500, 700), Color.White);
            spriteBatch.DrawString(font, y, new Vector2(600, 700), Color.White);
            spriteBatch.DrawString(font, z, new Vector2(700, 700), Color.White);

            spriteBatch.DrawString(font, other, new Vector2(10, 700), Color.White);

            spriteBatch.End();

            basicEffect.World = world;
            basicEffect.View = view;
            basicEffect.Projection = projection;
            basicEffect.VertexColorEnabled = true;


            vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), vertices.Count, BufferUsage.WriteOnly);
            vertexBuffer.SetData<VertexPositionColor>(vertices.ToArray());
            GraphicsDevice.SetVertexBuffer(vertexBuffer);

            //RasterizerState rasterizerState = new RasterizerState();
            //rasterizerState.CullMode = CullMode.None;
            //GraphicsDevice.RasterizerState = rasterizerState;

            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, 15);
                if (vertices.Count > 31)
                    GraphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 30, vertices.Count - 30 - 1);
            }

            base.Draw(gameTime);
        }

        public void CreateRectangle(int width, int height, out Texture2D rectangleTexture, Color color)
        {
            // create the rectangle texture, ,but it will have no color! lets fix that
            rectangleTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            // set the color to the amount of pixels
            Color[] colors = new Color[width * height];

            // loop through all the colors setting them to whatever values we want
            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = color;
            }

            // set the color data on the texture
            rectangleTexture.SetData(colors);

        }
    }
}
